// Copyright (c) 2024 Go-Frame-Lite
// Go-Frame-Lite is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//         http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

package gerror

import (
	"encoding/json"
	"net/http"
)

// Response 标准响应结构
// 类似于GF框架的response结构
// 提供统一的JSON响应格式
type Response struct {
	Code    int         `json:"code"`              // 错误码
	Message string      `json:"message"`           // 错误信息
	Data    interface{} `json:"data,omitempty"`    // 响应数据
	Detail  string      `json:"detail,omitempty"`  // 错误详情（开发环境）
	Stack   string      `json:"stack,omitempty"`   // 调用栈信息（开发环境）
}

// Success 成功响应
// 类似于GF框架的response.Success
func Success(data interface{}) *Response {
	return &Response{
		Code:    CodeOK,
		Message: GetMessage(CodeOK),
		Data:    data,
	}
}

// Error 错误响应
// 类似于GF框架的response.Error
func Error(code int, message ...string) *Response {
	msg := GetMessage(code)
	if len(message) > 0 && message[0] != "" {
		msg = message[0]
	}
	
	return &Response{
		Code:    code,
		Message: msg,
	}
}

// FromError 从错误创建响应
// 类似于GF框架的response.FromError
func FromError(err error, showDetail bool) *Response {
	if err == nil {
		return Success(nil)
	}
	
	if e, ok := err.(Error); ok {
		resp := &Response{
			Code:    e.Code(),
			Message: e.Message(),
		}
		
		if showDetail {
			resp.Detail = e.Detail()
			resp.Stack = e.Stack()
		}
		
		return resp
	}
	
	// 对于非标准错误，返回内部错误
	return Error(CodeInternalError, err.Error())
}

// JSON 将响应转换为JSON并写入HTTP响应
// 类似于GF框架的response.Json
func (r *Response) JSON(w http.ResponseWriter, statusCode int) error {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(statusCode)
	
	return json.NewEncoder(w).Encode(r)
}

// Write 写入HTTP响应，自动确定状态码
// 类似于GF框架的response.Write
func (r *Response) Write(w http.ResponseWriter) error {
	statusCode := http.StatusOK
	
	// 根据错误码确定HTTP状态码
	switch {
	case r.Code == CodeOK:
		statusCode = http.StatusOK
	case IsAuthError(r.Code):
		statusCode = http.StatusUnauthorized
	case IsParameterError(r.Code):
		statusCode = http.StatusBadRequest
	case IsDataError(r.Code) && r.Code == CodeDataNotFound:
		statusCode = http.StatusNotFound
	case IsDataError(r.Code):
		statusCode = http.StatusConflict
	case IsForbiddenError(r.Code):
		statusCode = http.StatusForbidden
	default:
		statusCode = http.StatusInternalServerError
	}
	
	return r.JSON(w, statusCode)
}

// IsForbiddenError 检查错误码是否属于禁止访问错误
func IsForbiddenError(code int) bool {
	return code == CodeForbidden || code == CodePermissionDenied
}

// HTTPStatusFromCode 根据错误码获取HTTP状态码
// 类似于GF框架的ghttp.StatusFromCode
func HTTPStatusFromCode(code int) int {
	switch {
	case code == CodeOK:
		return http.StatusOK
	case IsAuthError(code):
		return http.StatusUnauthorized
	case IsParameterError(code):
		return http.StatusBadRequest
	case IsDataError(code) && code == CodeDataNotFound:
		return http.StatusNotFound
	case IsDataError(code):
		return http.StatusConflict
	case IsForbiddenError(code):
		return http.StatusForbidden
	default:
		return http.StatusInternalServerError
	}
}

// HandlerFunc 包装Gin处理函数，提供标准错误处理
// 类似于GF框架的ghttp.HandlerFunc
type HandlerFunc func(*Context) error

// Context 扩展的上下文，包含标准响应功能
type Context struct {
	HTTPContext interface{} // 可以是gin.Context或其他HTTP框架的上下文
	ShowDetail  bool         // 是否显示详细错误信息
}

// NewContext 创建新的上下文
func NewContext(httpContext interface{}, showDetail bool) *Context {
	return &Context{
		HTTPContext: httpContext,
		ShowDetail:  showDetail,
	}
}

// Handler 创建HTTP处理函数
// 类似于GF框架的ghttp.MakeHandler
func Handler(fn HandlerFunc, showDetail bool) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, showDetail)
		
		// 这里可以根据具体HTTP框架设置上下文
		// 例如对于Gin框架：ctx.HTTPContext = gin.Context
		
		if err := fn(ctx); err != nil {
			resp := FromError(err, showDetail)
			resp.Write(w)
		}
	}
}